classdef system_base   < handle
 properties
     samplingTime = 1;
    m_utilization, m_servers, m_num_users;
    price_per_vm_hour;
    vm_instantiation_time;
    
    %------------- 
    workload=[]
    t=0;
    acc_pu=[];
    acc_inst=[];
    util=[];
    fired_inst = [];
    fired_t=[]; 
 end
 
 methods
     function obj=system_base
         addpath('opera'); 
         obj.t=0;
         obj.samplingTime = 60;
         obj.price_per_vm_hour=0.08; %dollars
         obj.vm_instantiation_time=8;  %minutes         
     end
     
     function past_acc_inst=past_inst(obj)
         past_acc_inst = obj.acc_inst(:,1:size(obj.util,1))';      
     end
     
     function acc_inst=future_inst(obj)
         acc_inst = obj.acc_inst(:,size(obj.util,1)+1:end)';      
     end
     
     function past_acc_inst=past_future_inst(obj)
         past_acc_inst = obj.acc_inst';      
     end

     function past_pu=past_pu(obj)
        past_pu = obj.acc_pu(:,1:size(obj.util,1))';     
     end
     
     function past_util=past_util(obj)
          past_util = obj.util;     
     end 
        
    % you decide on the number of servers 
    function util_=getNextUtil(obj,instance_quantity_to_fire)       
        demand = 0.09;
        util_=Inf;  % if model solving fails, this is going to be the value
        try
            opM=OpModel();
            CloudSimulate().buildtest2(opM,instance_quantity_to_fire, demand, obj.workload(obj.t));            
            opM.solve(); 
            %         util = [];
%         for i=3:length(opM.nodes)
%             util=[util opM.nodes(i).cpuUtilization];
%         end
%         util_ = mean(util);
            util_ = opM.scenarios(1).responseTime; 
        catch err
            disp('error occured in system_base.getNextUtil() during model solving');
        end  

        obj.util=[obj.util ; util_];
        obj.t=obj.t + 1;              
    end
%     
%      function acc_inst=accumulate(obj,acc_inst,t,instance_quantity_to_fire)
%                     if (t+60>size(acc_inst,1))
%                        acc_inst=[acc_inst ; zeros(t+60-size(acc_inst,1),size(instance_quantity_to_fire,2))];          
%                     end                   
%                    acc_inst(t+1:t+60,:)=acc_inst(t+1:t+60,:)+ones(60,1)*instance_quantity_to_fire;
%      end
        
     function acc_inst=destroy(obj,acc_inst,t,instance_quantity_to_destroy)
         if max(instance_quantity_to_destroy)==0
            return;
         end
         
         for i=1:size(acc_inst,1)       
             if instance_quantity_to_destroy(i)~=0
                 [row,col,v] =find(acc_inst(i,t:end)<acc_inst(i,t),1,'first');
                 if isempty(col)
                     % t to row
                     target=min(t+60,size(acc_inst,2));
                 else
                     target=col+t-1;
                 end
                 acc_inst(i,t:target-1) = acc_inst(i,t:target-1) - instance_quantity_to_destroy(i) ;
             end
         end
     end
    
    function util=getNextUtilAcc(obj,instance_quantity_to_fire,instance_quantity_to_destroy)
        % a=instance_quantity_to_fire*prices_amz().get_compute_units;
        t=obj.t;
        if (t+60>size(obj.acc_inst,2))
           obj.acc_inst=[obj.acc_inst zeros(size(instance_quantity_to_fire,2),t+60-size(obj.acc_inst,2))];          
        end
       obj.acc_inst(:,t:t+60)=obj.acc_inst(:,t:t+60)+instance_quantity_to_fire'*ones(1,61); 
       obj.acc_inst =  obj.destroy(obj.acc_inst,t,instance_quantity_to_destroy);
       if not(isempty(find(instance_quantity_to_fire>0)))
           obj.fired_inst = [obj.fired_inst; instance_quantity_to_fire]; 
           obj.fired_t=[obj.fired_t; obj.t]; 
       end
       util=obj.getNextUtil(obj.acc_inst(:,t)');                  
    end
    
    function [instance_quantity_to_fire, instance_quantity_to_destroy]=get_policy_output(obj,policy,t)
        [instance_quantity_to_fire,instance_quantity_to_destroy] = policy.get_choice_at_time(t);
    end
        
    function res=run_plan(obj,system,duration,policy)          
            qty=[]; utils=[]; 
            for t=1:duration-1               
                [instance_quantity_to_fire,instance_quantity_to_destroy]  = obj.get_policy_output(policy,t);                
                qty = [qty; instance_quantity_to_fire];
                %utils = [utils;
                %system.getNextUtilAcc(instance_quantity_to_fire)];      %one server                    
                util = system.getNextUtilAcc(instance_quantity_to_fire,instance_quantity_to_destroy);
                disp(sprintf('t=%d',obj.t));  
                disp(sprintf('%s->%6.2f', vect2str(obj.acc_inst(:,obj.t)','formatString','%d','separator',' '), util));                 
            end            
            res=struct('qty',qty);
    end
    
    function n=initialize(obj,length_in_minutes)
        obj.t = 1;              
        
        % load the input of proper size and linearize it
       % length_in_minutes=219;         % assume 
      %  load a_sample.mat;
       % n= num_users((1:length_in_minutes))';          %load length_in_minutes of data from fifa
     %   n=workload().get_workload('data/day42_per_min.txt', 1, length_in_minutes);
        n= workload().get_workload('data/day66_per_min.txt', 1, 23*60);
        n=n(600:600+length_in_minutes);
        obj.workload=n';
    end

    function inst_cost = inst_cost(obj,inst_vec)
            inst_cost = inst_vec * (prices_amz().get_hourly_price) ;
        end
            
    function [cost,cost1,penalty]=cost(obj, inst_vec, util, inst_interval)              
              target_util = 60; % 0.8 utilizattion 
              util_violation =  util - target_util .* ones(size(util,1),size(util,2));
%               tmp(tmp>0)=0;
%               penalty = sum(-1*tmp) ; 
              util_violation(util_violation<0)=0;
              
              % hack: when model does not recognize there is no instance to
              % cover within the next control interval 
              for tt=1:obj.control_interval
                  if inst_interval(tt+60,:)==zeros(1,11)                      
                        util_violation(tt)=+inf;
                  end
              end
                  
              penalty = sum(util_violation) ; 
              cost1 =  obj.inst_cost(inst_vec); 
              % here we are adding (delta of dollars(instance hourly price
              % (e.g. 0.08 for small))) to (utilization deviation (e.g. 65))
              cost=100*cost1+1*penalty;              
%               disp(strcat('util:',num2str(util'))); 
%               disp(strcat('tmp:',num2str(tmp'))); 
%               disp(strcat('penalty:',num2str(penalty))); 
%               disp(strcat('cost1:',num2str(cost1))); 
%               disp(strcat('cost:',num2str(cost))); 

    end
        
    % u=system_base().testGetNextUtilAcc
    function utils=testGetNextUtilAcc(obj)
        duration=2; % 2*60;
        workload = obj.initialize(duration);
        subplot(2,1,1);   plot(workload);
        utils=[];
        for i=1:duration-1
            if i==1
                instance_quantity_to_fire=[0 4 0 0 0 0 0 0 0 0 0 ];
            else
                instance_quantity_to_fire=[0 0 0 0 0 0 0 0 0 0 0 ];
            end
            % disp(strcat('time is ',num2str(i)));
            utils = [utils  obj.getNextUtilAcc(instance_quantity_to_fire)];      %one server
        end
       % subplot(2,1,2);  plot(utils,'g--');          
    end
    
    function [rt,cost]=test_opt(obj)
         duration=2*60;
        target_rt=2.4;
        % minimize cost
        workload = obj.initialize(duration); 
        subplot(2,1,1);   plot(workload);
        const_sat=false;
        i=12;
        rt=[];
        cost=[];
        while const_sat==false
            i=i+1;
            instance_quantity_to_fire=[0 0 0 0 0 0 0 0 1 0 0 ];
            try
               rt = [rt ; obj.getNextUtilAcc(instance_quantity_to_fire)];
            catch err
                disp('error occured');
                rt=[rt ; Inf]; 
            end  % end try/catch
            cost = [cost ; instance_quantity_to_fire*prices_amz().get_hourly_price]; 
            const_sat=(rt(end)<target_rt);
        end
            
    end
    
%     function [num,target]=bin_search(obj,constraints,num,f)
%         C =  constraints; 
%         % C = bitget(uint8(constraints), 8:-1:1); 
%         i=find(C==0);  % add aconstraint
%         C(i(1))=1;
%         if @f( bitand(uint8(C), uint8(num))) > target
%             
%         end
%         
%     end
    
    function plot_compare_simple(obj,y1,y2)
        plot(obj.m_utilization + y1.OutputData,'g--');        hold on; 
        plot(obj.m_utilization + y2.OutputData,'r--');      
    end
    
    function plotit(obj,y,yn)
%         scatter(data.OutputData,y.OutputData);
%         plot( y.OutputData,'r--'); hold on; plot(data.OutputData);

        subplot(3,1,1); 
        plot(m_utilization + y.OutputData,'g--');        hold on; 
        plot(m_utilization + yn.OutputData,'r--');         hold on; 
        plot(m_utilization +data.OutputData);
        title('measured/modeled Utilizations')

        subplot(3,1,2); 
        plot(m_servers+data.InputData(:,1))
        title('#Servers')

        subplot(3,1,3); 
        plot(m_num_users+data.InputData(:,2))
        title('#Users')
    end
            
    function plot_data(obj)
        load a_sample.mat        
        figure;
        subplot(3,1,1);   plot(num_users);
        subplot(3,1,2);  plot(servers(3,:)')
        subplot(3,1,3);  plot(utilization(3,:)')
    end
        
 end
end



